package automatagen;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;

import automata.Address;
import automata.Address2;
import automata.HeaderTDCAutomata;
import automata.HeaderTDCAutomataEngine;
import automata.HeaderTDCAutomataSpecific;
import automata.MainExample;
import automata.Makefile;
import automata.MakefileDep;
import automata.TDCAutomata;
import automata.TDCAutomataEngine;
import automata.TDCAutomataSpecific;


public class AutomataGen {
    private HashMap<String, Automata> automatas = new HashMap<String,Automata>();
	private Integer baseAddress;
	private String absoluteDir;
	private String projName;
	private HashMap<String,RegisterMap> regs = new HashMap<String,RegisterMap>();
	private HashMap<String,VarMapDefinition> varMaps = new HashMap<String, VarMapDefinition>();
	private HashMap<String,DataFormat> formats = new HashMap<String,DataFormat>();
	private HashMap<String,String> formatMapReg = new HashMap<String,String>();
	private HashMap<String,AccessMethod> accessMethods = new HashMap<String,AccessMethod>();
	private BufferedWriter bw;
	private File tmpFile;
	
	public String getAbsoluteDir() {
		return absoluteDir;
	}
	public String getProjName() {
		return projName;
	}
	
    public HashMap<String,Automata> getAutomatas() {
		return automatas;
	}
	public Integer getBaseAddress() {
		return baseAddress;
	}
	public HashMap<String, RegisterMap> getRegs() {
		return regs;
	}
	public HashMap<String, VarMapDefinition> getVarMaps() {
		return varMaps;
	}
	public HashMap<String, DataFormat> getFormats() {
		return formats;
	}
	public HashMap<String, String> getFormatMapReg() {
		return formatMapReg;
	}
	public HashMap<String, AccessMethod> getAccessAccessMethod() {
		return accessMethods;
	}
	
	public void addAccessMethod(String map, AccessMethod accessMethod){
		this.accessMethods.put(map,accessMethod);
	}
	
	public void addVarMapDefinition(VarMapDefinition varmaps){
		varMaps.put(varmaps.getName(), varmaps);
	}
	
	public void addFormat(DataFormat dtFormat){
		formats.put(dtFormat.getName(), dtFormat);
	}
	
	public void addFormatMapReg(String map){
		String[] pieces = map.split(":");
		formatMapReg.put(pieces[0], pieces[1]);
	}
	
	public AutomataGen(MethodDefinition methodDef, String projName, Integer baseAddress, String absoluteDir) {
		this.automatas = methodDef.getAutomatas();
		this.regs = methodDef.getRegisters();
		this.baseAddress = baseAddress;
		this.absoluteDir = absoluteDir;
		this.projName = projName;
	}
	
	private void genFile(boolean header, String filename, String body) throws IOException{
		if (header){
			tmpFile = new File(this.getAbsoluteDir() + this.projName + "/headers/" + filename);
			bw = new BufferedWriter(new FileWriter(tmpFile));
			bw.write(body);
			bw.flush();
			bw.close();
		}else{
			tmpFile = new File(this.getAbsoluteDir() + this.projName + "/" + filename);
			bw = new BufferedWriter(new FileWriter(tmpFile));
			bw.write(body);
			bw.flush();
			bw.close();
		}
		System.out.print(".");
	}
	
	private void printHeader(){
		System.out.println();
		System.out.println();
		System.out.println("#################################");
		System.out.println("##    DevC Temporal Checker    ##");
		System.out.println("#################################");
		System.out.println("##      Greco/CIn - UFPE       ##");
		System.out.println("## http://cin.ufpe.br/~greco/  ##");
		System.out.println("#################################");
		System.out.println("##   By Rafael Melo Macieira   ##");
		System.out.println("##      rmm2@cin.ufpe.br       ##");
		System.out.println("#################################");
		System.out.println();
		System.out.println("** Starting **");
		System.out.println();
		System.out.println("** Project: " + this.getProjName());
		System.out.println("** Output Dir: " + this.getAbsoluteDir() + this.getProjName() + "/");
		System.out.println("** Device Base Address: " + this.getBaseAddress());
		System.out.println();
	}
	
	public void generateAutomatasCode(){
		for (String reg : this.regs.keySet()) {
			this.regs.get(reg).setFormat(new FormatPattern(formats.get(formatMapReg.get(reg)).getPattern()));
		}
		printHeader();
		TDCAutomataSpecific automataSpecificClass = new TDCAutomataSpecific();
		HeaderTDCAutomataSpecific headerAutomataSpecifc= new HeaderTDCAutomataSpecific();
		TDCAutomataEngine automataEngine = new TDCAutomataEngine();
		HeaderTDCAutomataEngine headerAutomataEngine = new HeaderTDCAutomataEngine();
		TDCAutomata automataClass = new TDCAutomata();
		HeaderTDCAutomata headerAutomataClass = new HeaderTDCAutomata();
		Makefile makefile = new Makefile();
		Address address = new Address();
		MakefileDep makefileDep = new MakefileDep();
		MainExample mainExample = new MainExample(); 
		
		String automataSpecificClassResult  = new String();
		String automataSpecificHeaderResult = new String();
		String automataEngineResult = new String();
		String automataHeaderEngineResult  = new String();
		String automataHeaderClassResult = new String();
		String automataClassResult = new String();
		String addressResult = new String();
		String makefileDepResult = new String();
		String makefileResult = new String();
		String mainResult = new String();
		ArrayList<Tupla> automataBody = new ArrayList<Tupla>();
		
		try {
			System.out.print("** Generating Files..");
			tmpFile = new File(this.getAbsoluteDir() + this.projName + "/");
			if (!tmpFile.exists()) {
				tmpFile.mkdir();
				tmpFile = new File(this.getAbsoluteDir() + this.projName + "/headers/");
				tmpFile.mkdir();
				tmpFile = new File(this.getAbsoluteDir() + this.projName + "/build/");
				tmpFile.mkdir();
			}
			Automata automata;
			for (String service : automatas.keySet()) {
			    automata = automatas.get(service);
				automata.setAutomataGen(this);
				automataSpecificClassResult = automataSpecificClass.generate(automata);
				automataSpecificHeaderResult = headerAutomataSpecifc.generate(automata);
				genFile(false,"tdc_automata_" + automata.getName() + ".cpp",automataSpecificClassResult);
				genFile(true,"tdc_automata_" + automata.getName() + ".h",automataSpecificHeaderResult);
			}
			
			addressResult = address.generate(this);
			makefileDepResult = makefileDep.generate(this);
			automataClassResult = automataClass.generate(this);
			automataHeaderClassResult = headerAutomataClass.generate(this);
			automataEngineResult = automataEngine.generate(this);
			automataHeaderEngineResult = headerAutomataEngine.generate(this);
			makefileResult = makefile.generate(this);
			mainResult = mainExample.generate(this);
			
			genFile(false,"tdc_automata.cpp",automataClassResult);
			genFile(true,"tdc_automata.h",automataHeaderClassResult);
			genFile(false,"tdc_automata_engine.cpp",automataEngineResult);
			genFile(true,"tdc_automata_engine.h",automataHeaderEngineResult);
			genFile(true,"address.sh",addressResult);
			genFile(true,"makefiledep",makefileDepResult);
			genFile(false,"Makefile",makefileResult);

			//MAIN TESTE//
			genFile(false,"main.cpp",mainResult);
			//genFile(false,"main.cpp","#include \"headers/tdc_automata.h\"\n#include \"headers/tdc_automata_engine.h\"\n#include <stdio.h>\n\nint main(){\nprintf(\"TESTE\\n\");\nreturn 0;\n}");
			//////////////
			
			System.out.println("Done.");
			System.out.println();
			System.out.println("** Success!");
			System.out.println();
			System.out.println();
		}catch(IOException ioe){
			System.out.println("IO Error!");
			System.out.println();
			System.out.println("ERROR: [" + ioe.getMessage() + "]");
			System.out.println();
			System.out.println("** Fail!");
			System.out.println();
			System.out.println();
		}
	}

}
